#!/bin/bash
#
# The script decompiles the given file into the selected target high-level language.
#

SCRIPTPATH="$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )"

if [ -z "$DECOMPILER_UTILS" ]; then
	DECOMPILER_UTILS="$SCRIPTPATH/utils.sh"
fi

. "$DECOMPILER_UTILS"

#
# Print help.
#
print_help()
{
	echo "Decompiles the given file into the selected target high-level language."
	echo ""
	echo "Usage:"
	echo "    $0 [ options ] file"
	echo ""
	echo "Options:"
	echo "    -a name,   --arch name                            Specify target architecture [mips|pic32|arm|thumb|powerpc|x86] (default: x86)."
	echo "    -e name,   --endian name                          Specify target endianness [little|big] (default: little). Not all combinations are supported."
	echo "    -f name,   --format name                          Specify object file format [elf|pe|ihex|macho] (default: pe)."
	echo "    -h,        --help                                 Print this help message."
	echo "    -k         --keep-unreachable-funcs               Keep functions that are unreachable from the main function."
	echo "    -l string, --target-language string               Target high-level language [c|py] (default: c)."
	echo "    -m name,   --mode name                            Force the type of decompilation mode [bin|ll|raw] (default: ll if input's suffix is '.ll', bin otherwise)."
	echo "    -o file,   --output file                          Output file (default: file.ext)."
	echo "    -p file,   --pdb file                             File with PDB debug information."
	echo "               --ar-index name                        Pick file from archive for decompilation by its zero-based index."
	echo "               --ar-name string                       Pick file from archive for decompilation by its name."
	echo "               --backend-aggressive-opts              Enables aggressive optimizations."
	echo "               --backend-arithm-expr-evaluator string Name of the used evaluator of arithmetical expressions (default: c)."
	echo "               --backend-call-info-obtainer string    Name of the obtainer of information about function calls (default: optim)."
	echo "               --backend-cfg-test                     Unifies the labels of all nodes in the emitted CFG (this has to be used in tests)."
	echo "               --backend-disabled-opts list           Prevents the optimizations from the given comma-separated list of optimizations to be run."
	echo "               --backend-emit-cfg                     Emits a CFG for each function in the backend IR (in the .dot format)."
	echo "               --backend-emit-cg                      Emits a CG for the decompiled module in the backend IR (in the .dot format)."
	echo "               --backend-cg-conversion string         Should the CG from the backend be converted automatically into the desired format? [auto|manual] (default: auto)."
	echo "               --backend-cfg-conversion string        Should CFGs from the backend be converted automatically into the desired format? [auto|manual] (default: auto)."
	echo "               --backend-enabled-opts list            Runs only the optimizations from the given comma-separated list of optimizations."
	echo "               --backend-find-patterns list           Runs the finders of patterns specified in the given comma-separated list (use 'all' to run them all)."
	echo "               --backend-force-module-name string     Overwrites the module name that was detected/generated by the front-end."
	echo "               --backend-keep-all-brackets            Keeps all brackets in the generated code."
	echo "               --backend-keep-library-funcs           Keep functions from standard libraries."
	echo "               --backend-llvmir2bir-converter string  Name of the converter from LLVM IR to BIR (default: orig)."
	echo "               --backend-no-compound-operators        Do not emit compound operators (like +=) instead of assignments."
	echo "               --backend-no-debug                     Disables the emission of debug messages, such as phases."
	echo "               --backend-no-debug-comments            Disables the emission of debug comments in the generated code."
	echo "               --backend-no-opts                      Disables backend optimizations."
	echo "               --backend-no-symbolic-names            Disables the conversion of constant arguments to their symbolic names."
	echo "               --backend-no-time-varying-info         Do not emit time-varying information, like dates."
	echo "               --backend-no-var-renaming              Disables renaming of variables in the backend."
	echo "               --backend-semantics                    A comma-separated list of the used semantics."
	echo "               --backend-strict-fpu-semantics         Forces strict FPU semantics to be used."
	echo "               --backend-var-renamer string           Used renamer of variables [address|hungarian|readable|simple|unified] (default: readable)."
	echo "               --cleanup                              Removes temporary files created during the decompilation."
	echo "               --color-for-ida                        Put IDA Pro color tags to output C file."
	echo "               --config name                          Specify JSON decompilation configuration file."
	echo "               --no-config                            State explicitly that config file is not to be used."
	echo "               --fileinfo-verbose                     Print all detected information about input file."
	echo "               --fileinfo-use-all-external-patterns   Use all detection rules from external YARA databases."
	echo "               --graph-format name                    Specify format of a all generated graphs (e.g. CG, CFG) [pdf|png|svg] (default: png)."
	echo "               --raw-entry-point addr                 Entry point address used for raw binary (default: architecture dependent)."
	echo "               --raw-section-vma addr                 Virtual address where section created from the raw binary will be placed (default: architecture dependent)."
	echo "               --select-decode-only                   Decode only selected parts (functions/ranges). Faster decompilation, but worse results."
	echo "               --select-functions list                Specify a comma separated list of functions to decompile (example: fnc1,fnc2,fnc3)."
	echo "               --select-ranges list                   Specify a comma separated list of ranges to decompile (example: 0x100-0x200,0x300-0x400,0x500-0x600)."
	echo "               --stop-after tool                      Stop the decompilation after the given tool (supported tools: fileinfo, unpacker, bin2llvmir, llvmir2hll)."
	echo "               --static-code-sigfile path             Adds additional signature file for static code detection."
	echo "               --static-code-archive path             Adds additional signature file for static code detection from given archive."
	echo "               --no-default-static-signatures         No default signatures for statically linked code analysis are loaded (options static-code-sigfile/archive are still available)."
}
SCRIPT_NAME=$0
GETOPT_SHORTOPT="a:e:f:hkl:m:o:p:"
GETOPT_LONGOPT="arch:,format:,help,keep-unreachable-funcs,target-language:,mode:,output:,pdb:,backend-aggressive-opts,backend-arithm-expr-evaluator:,backend-call-info-obtainer:,backend-cfg-test,backend-disabled-opts:,backend-emit-cfg,backend-emit-cg,backend-cg-conversion:,backend-cfg-conversion:,backend-enabled-opts:,backend-find-patterns:,backend-force-module-name:,backend-keep-all-brackets,backend-keep-library-funcs,backend-llvmir2bir-converter:,backend-no-compound-operators,backend-no-debug,backend-no-debug-comments,backend-no-opts,backend-no-symbolic-names,backend-no-time-varying-info,backend-no-var-renaming,backend-semantics,backend-strict-fpu-semantics,backend-var-renamer:,cleanup,graph-format:,raw-entry-point:,raw-section-vma:,endian:,select-decode-only,select-functions:,select-ranges:,fileinfo-verbose,fileinfo-use-all-external-patterns,config:,color-for-ida,no-config,stop-after:,static-code-sigfile:,static-code-archive:,no-default-static-signatures,ar-name:,ar-index:"

#
# Check proper combination of input arguments.
#
check_arguments()
{
	# Check whether the input file was specified.
	if [ -z "$IN" ]; then
		print_error_and_die "No input file was specified"
	fi

	# Try to detect desired decompilation mode if not set by user.
	# We cannot detect "raw" mode because it overlaps with "bin" (at least not based on extension).
	if [ -z "$MODE" ]; then
		if [[ ${IN: -3} == ".ll" ]]; then	# Suffix .ll
			MODE="ll"
		else
			MODE="bin"
		fi
	fi

	# Print warning message about unsupported combinations of options.
	if [ "$MODE" = "ll" ]; then
		[ "$ARCH" ] && print_warning "Option -a|--arch is not used in mode $MODE"
		[ "$FORMAT" ] && print_warning "Option -f|--format is not used in mode $MODE"
		[ "$PDB_FILE" ] && print_warning "Option -p|--pdb is not used in mode $MODE"
		[ "$CONFIG_DB" = "" ] && [ ! "$NO_CONFIG" ] && print_error_and_die "Option --config or --no-config must be specified in mode $MODE"
	elif [ "$MODE" = "raw" ]; then
		# Errors -- missing critical arguments.
		[ ! "$ARCH" ] && print_error_and_die "Option -a|--arch must be used with mode $MODE"
		[ ! "$ENDIAN" ] && print_error_and_die "Option -e|--endian must be used with mode $MODE"
		[ ! "$RAW_ENTRY_POINT" ] && print_error_and_die "Option --raw-entry-point must be used with mode $MODE"
		[ ! "$RAW_SECTION_VMA" ] && print_error_and_die "Option --raw-section-vma must be used with mode $MODE"
		if ! is_number "$RAW_ENTRY_POINT"; then
			print_error_and_die "Value in option --raw-entry-point must be decimal (e.g. 123) or hexadecimal value (e.g. 0x123)"
		fi
		if ! is_number "$RAW_SECTION_VMA";then
			print_error_and_die "Value in option --raw-section-vma must be decimal (e.g. 123) or hexadecimal value (e.g. 0x123)"
		fi
		# Warnings -- bad combinations of arguments, but not critical.
		[ "$FORMAT" ] && print_warning "Option -f|--format is not used in mode $MODE"
	fi

	# Intel HEX format needs architecture and endian to be specified.
	if [ "$FORMAT" = "ihex" ]; then
		[ ! "$ARCH" ] && print_error_and_die "Option -a|--arch must be used with format $FORMAT"
		[ "$MODE" = "bin" ] && [ ! "$ENDIAN" ] && print_error_and_die "Option -e|--endian must be used with format $FORMAT and mode $MODE"
		[ "$ARCH" != "mips" ] && [ "$ARCH" != "pic32" ] && print_error_and_die "Format $FORMAT can be used only with mips and pic32 architectures"
	fi

	# Archive decompilation errors.
	[ "$AR_NAME" ] && [ "$AR_INDEX" ] && print_error_and_die "Options --ar-name and --ar-index are mutually exclusive. Pick one."
	if [ "$MODE" != "bin" ]; then
		[ "$AR_NAME" ] && print_warning "Option --ar-name is not used in mode $MODE"
		[ "$AR_INDEX" ] && print_warning "Option --ar-index is not used in mode $MODE"
	fi

	# Conditional initialization.
	HLL=${HLL:=c}

	if [ -z "$OUT" ]; then
		# No output file was given, so use the default one.
		if [ "${IN##*.}" = "ll" ]; then	# Suffix .ll
			OUT=${IN%.ll}".$HLL"
		elif [ "${IN##*.}" = "exe" ]; then	# Suffix .exe
			OUT=${IN%.exe}".$HLL"
		elif [ "${IN##*.}" = "elf" ]; then	# Suffix .elf
			OUT=${IN%.elf}".$HLL"
		elif [ "${IN##*.}" = "ihex" ]; then	# Suffix .ihex
			OUT=${IN%.ihex}".$HLL"
		elif [ "${IN##*.}" = "macho" ]; then	# Suffix .macho
			OUT=${IN%.macho}".$HLL"
		else
			OUT=${IN}"$PICKED_FILE.$HLL"
		fi
	fi

	# If the output file name matches the input file name, we have to change the
	# output file name. Otherwise, the input file gets overwritten.
	if [ "$IN" = "$OUT" ]; then
		OUT=${IN%.*}".out.$HLL"
	fi

	# Convert to absolute paths.
	IN=$(readlink -f "$IN")
	OUT=$(readlink -f "$OUT")
	if [ -e "$PDB_FILE" ]; then
		PDB_FILE=$(readlink -f "$PDB_FILE")
	fi

	# Check that selected ranges are valid.
	if [ "$SELECTED_RANGES" ]; then
		for r in "${SELECTED_RANGES[@]}"; do
			# Check if valid range.
			if ! is_range "$r"; then
				print_error_and_die "Range '$r' in option --select-ranges is not a valid decimal (e.g. 123-456) or hexadecimal (e.g. 0x123-0xabc) range."
			fi

			# Check if first <= last.
			IFS='-' read -a vs <<< "$r"  # parser line into array
			if (( ${vs[0]} > ${vs[1]} )); then
				print_error_and_die "Range '$r' in option --select-ranges is not a valid range: second address must be greater or equal than the first one."
			fi
		done
	fi
}

#
# Prints a warning if we are decompiling bytecode.
#
print_warning_if_decompiling_bytecode()
{
	BYTECODE=$($CONFIGTOOL "$CONFIG" --read --bytecode)
	if [ "$BYTECODE" != "" ]; then
		print_warning "Detected $BYTECODE bytecode, which cannot be decompiled by our machine-code decompiler. The decompilation result may be inaccurate."
	fi
}

#
# Checks whether the decompilation should be forcefully stopped because of the
# --stop-after parameter. If so, cleanup is run and the script exits with 0.
#
# Arguments:
#
#   $1 Name of the tool.
#
# The function expects the $STOP_AFTER variable to be set.
#
check_whether_decompilation_should_be_forcefully_stopped()
{
	if [ "$STOP_AFTER" = "$1" ]; then
		cleanup
		echo ""
		echo "#### Forced stop due to '--stop-after $STOP_AFTER'..."
		exit 0
	fi
}

#
# Clean working directory.
#
cleanup()
{
	if [ "$CLEANUP" ]; then
		rm -f "$OUT_UNPACKED"
		rm -f "$OUT_FRONTEND_LL"
		rm -f "$OUT_FRONTEND_BC"
		if [ "$CONFIG" != "$CONFIG_DB" ]; then
			rm -f "$CONFIG"
		fi
		rm -f "$OUT_BACKEND_BC"
		rm -f "$OUT_BACKEND_LL"
		rm -f "$OUT_RAW_EXECUTABLE"		# Mode "raw"
		rm -f "$OUT_RESTORED"			# Archive support
		rm -f "$OUT_ARCHIVE"			# Archive support (Macho-O Universal)
		rm -f "${SIGNATURES_TO_REMOVE[@]}"	# Signatures generated from archives
	fi
}

# Check script arguments.
PARSED_OPTIONS=$(getopt -o "$GETOPT_SHORTOPT" -l "$GETOPT_LONGOPT" -n "$SCRIPT_NAME" -- "$@")

# Bad arguments.
[ $? -ne 0 ] && print_error_and_die "Getopt - parsing parameters fail"

eval set -- "$PARSED_OPTIONS"

while true ;
do
	case "$1" in
	-a|--arch)						# Target architecture.
		[ "$ARCH" ] && print_error_and_die "Duplicate option: -a|--arch"
		[ "$2" != "mips" -a "$2" != "pic32" -a "$2" != "arm" -a "$2" != "thumb" -a "$2" != "powerpc" -a "$2" != "x86" ] && print_error_and_die "Unsupported target architecture '$2'. Supported architectures: Intel x86, ARM, ARM+Thumb, MIPS, PIC32, PowerPC."
		ARCH="$2"
		shift 2;;
	-e|--endian)				# Endian.
		[ "$ENDIAN" ] && print_error_and_die "Duplicate option: -e|--endian"
		ENDIAN="$2"
		shift 2;;
	-f|--format)					# Executable file format.
		[ "$FORMAT" ] && print_error_and_die "Duplicate option: -f|--format"
		[ "$2" != "elf" -a "$2" != "pe" -a "$2" != "ihex" -a "$2" != "macho" ] && print_error_and_die "Unsupported target format '$2'. Supported formats: ELF, PE, Intel HEX, Mach-O."
		FORMAT="$2"
		shift 2;;
	-h|--help) 					# Help.
		print_help
		exit 0;;
	-k|--keep-unreachable-funcs) 	# Keep unreachable functions.
		# Do not check if this parameter is a duplicate because when both
		# --select-ranges or --select--functions and -k is specified, the
		# decompilation fails.
		KEEP_UNREACHABLE_FUNCS=1
		shift;;
	-l|--target-language)			# Target language.
		[ "$HLL" ] && print_error_and_die "Duplicate option: -l|--target-language"
		[ "$2" != "c" -a "$2" != "py" ] && print_error_and_die "Unsupported target language '$2'. Supported languages: C, Python."
		HLL="$2"
		shift 2;;
	-m|--mode)						# Decompilation mode.
		[ "$MODE" ] && print_error_and_die "Duplicate option: -m|--mode"
		[ "$2" != "bin" -a "$2" != "ll" -a "$2" != "raw" ] && print_error_and_die "Unsupported decompilation mode '$2'. Supported modes: bin, ll, raw."
		MODE="$2"
		shift 2;;
	-o|--output)					# Output file.
		[ "$OUT" ] && print_error_and_die "Duplicate option: -o|--output"
		OUT=$2
		shift 2;;
	-p|--pdb)						# File containing PDB debug information.
		[ "$PDB_FILE" ] && print_error_and_die "Duplicate option: -p|--pdb"
		PDB_FILE="$2"
		if [ ! -r "$PDB_FILE" ]; then
			print_error_and_die "The input PDB file '$PDB_FILE' does not exist or is not readable"
		fi
		shift 2;;
	--backend-aggressive-opts)		# Enable aggressive optimizations.
		[ "$BACKEND_AGGRESSIVE_OPTS" ] && print_error_and_die "Duplicate option: --backend-aggressive-opts"
		BACKEND_AGGRESSIVE_OPTS=1
		shift;;
	--backend-arithm-expr-evaluator) # Name of the evaluator of arithmetical expressions.
		[ "$BACKEND_ARITHM_EXPR_EVALUATOR" ] && print_error_and_die "Duplicate option: --backend-arithm-expr-evaluator"
		BACKEND_ARITHM_EXPR_EVALUATOR="$2"
		shift 2;;
	--backend-call-info-obtainer)	# Name of the obtainer of information about function calls.
		[ "$BACKEND_CALL_INFO_OBTAINER" ] && print_error_and_die "Duplicate option: --backend-call-info-obtainer"
		BACKEND_CALL_INFO_OBTAINER="$2"
		shift 2;;
	--backend-cfg-test)				# Unify the labels in the emitted CFG.
		[ "$BACKEND_CFG_TEST" ] && print_error_and_die "Duplicate option: --backend-cfg-test"
		BACKEND_CFG_TEST=1
		shift;;
	--backend-disabled-opts) 		# List of disabled optimizations in the backend.
		[ "$BACKEND_DISABLED_OPTS" ] && print_error_and_die "Duplicate option: --backend-disabled-opts"
		BACKEND_DISABLED_OPTS="$2"
		shift 2;;
	--backend-emit-cfg)				# Emit a CFG of each function in the backend IR.
		[ "$BACKEND_EMIT_CFG" ] && print_error_and_die "Duplicate option: --backend-emit-cfg"
		BACKEND_EMIT_CFG=1
		shift;;
	--backend-emit-cg)				# Emit a CG of the decompiled module in the backend IR.
		[ "$BACKEND_EMIT_CG" ] && print_error_and_die "Duplicate option: --backend-emit-cg"
		BACKEND_EMIT_CG=1
		shift;;
	--backend-cg-conversion)			# Should the CG from the backend be converted automatically into the desired format?.
		[ "$BACKEND_CG_CONVERSION" ] && print_error_and_die "Duplicate option: --backend-cg-conversion"
		[ "$2" != "auto" -a "$2" != "manual" ] && print_error_and_die "Unsupported CG conversion mode '$2'. Supported modes: auto, manual."
		BACKEND_CG_CONVERSION="$2"
		shift 2;;
	--backend-cfg-conversion)			# Should CFGs from the backend be converted automatically into the desired format?.
		[ "$BACKEND_CFG_CONVERSION" ] && print_error_and_die "Duplicate option: --backend-cfg-conversion"
		[ "$2" != "auto" -a "$2" != "manual" ] && print_error_and_die "Unsupported CFG conversion mode '$2'. Supported modes: auto, manual."
		BACKEND_CFG_CONVERSION="$2"
		shift 2;;
	--backend-enabled-opts)			# List of enabled optimizations in the backend.
		[ "$BACKEND_ENABLED_OPTS" ] && print_error_and_die "Duplicate option: --backend-enabled-opts"
		BACKEND_ENABLED_OPTS="$2"
		shift 2;;
	--backend-find-patterns) 		# Try to find patterns.
		[ "$BACKEND_FIND_PATTERNS" ] && print_error_and_die "Duplicate option: --backend-find-patterns"
		BACKEND_FIND_PATTERNS="$2"
		shift 2;;
	--backend-force-module-name)	# Force the module's name in the backend.
		[ "$BACKEND_FORCED_MODULE_NAME" ] && print_error_and_die "Duplicate option: --backend-force-module-name"
		BACKEND_FORCED_MODULE_NAME="$2"
		shift 2;;
	--backend-keep-all-brackets)	# Keep all brackets.
		[ "$BACKEND_KEEP_ALL_BRACKETS" ] && print_error_and_die "Duplicate option: --backend-keep-all-brackets"
		BACKEND_KEEP_ALL_BRACKETS=1
		shift;;
	--backend-keep-library-funcs) 	# Keep library functions.
		[ "$BACKEND_KEEP_LIBRARY_FUNCS" ] && print_error_and_die "Duplicate option: --backend-keep-library-funcs"
		BACKEND_KEEP_LIBRARY_FUNCS=1
		shift;;
	--backend-llvmir2bir-converter)	# Name of the converter of LLVM IR to BIR.
		[ "$BACKEND_LLVMIR2BIR_CONVERTER" ] && print_error_and_die "Duplicate option: --backend-llvmir2bir-converter"
		BACKEND_LLVMIR2BIR_CONVERTER="$2"
		shift 2;;
	--backend-no-compound-operators) # Do not use compound operators.
		[ "$BACKEND_NO_COMPOUND_OPERATORS" ] && print_error_and_die "Duplicate option: --backend-no-compound-operators"
		BACKEND_NO_COMPOUND_OPERATORS=1
		shift;;
	--backend-no-debug)				# Emission of debug messages.
		[ "$BACKEND_NO_DEBUG" ] && print_error_and_die "Duplicate option: --backend-no-debug"
		BACKEND_NO_DEBUG=1
		shift;;
	--backend-no-debug-comments) 	# Emission of debug comments.
		[ "$BACKEND_NO_DEBUG_COMMENTS" ] && print_error_and_die "Duplicate option: --backend-no-debug-comments"
		BACKEND_NO_DEBUG_COMMENTS=1
		shift;;
	--backend-no-opts)				# Disable backend optimizations.
		[ "$BACKEND_OPTS_DISABLED" ] && print_error_and_die "Duplicate option: --backend-no-opts"
		BACKEND_OPTS_DISABLED=1
		shift;;
	--backend-no-symbolic-names) 	# Disable the conversion of constant arguments.
		[ "$BACKEND_NO_SYMBOLIC_NAMES" ] && print_error_and_die "Duplicate option: --backend-no-symbolic-names"
		BACKEND_NO_SYMBOLIC_NAMES=1
		shift;;
	--backend-no-time-varying-info)	# Do not emit any time-varying information.
		[ "$BACKEND_NO_TIME_VARYING_INFO" ] && print_error_and_die "Duplicate option: --backend-no-time-varying-info"
		BACKEND_NO_TIME_VARYING_INFO=1
		shift;;
	--backend-no-var-renaming)		# Disable renaming of variables in the backend.
		[ "$BACKEND_VAR_RENAMING_DISABLED" ] && print_error_and_die "Duplicate option: --backend-no-var-renaming"
		BACKEND_VAR_RENAMING_DISABLED=1
		shift;;
	--backend-semantics) 			# The used semantics in the backend.
		[ "$BACKEND_SEMANTICS" ] && print_error_and_die "Duplicate option: --backend-semantics"
		BACKEND_SEMANTICS="$2"
		shift 2;;
	--backend-strict-fpu-semantics)	# Use strict FPU semantics in the backend.
		[ "$BACKEND_STRICT_FPU_SEMANTICS" ] && print_error_and_die "Duplicate option: --backend-strict-fpu-semantics"
		BACKEND_STRICT_FPU_SEMANTICS=1
		shift;;
	--backend-var-renamer)			# Used renamer of variable names.
		[ "$BACKEND_VAR_RENAMER" ] && print_error_and_die "Duplicate option: --backend-var-renamer"
		[ "$2" != "address" -a "$2" != "hungarian" -a "$2" != "readable" -a "$2" != "simple" -a "$2" != "unified" ] && print_error_and_die "Unsupported variable renamer '$2'. Supported renamers: address, hungarian, readable, simple, unified."
		BACKEND_VAR_RENAMER="$2"
		shift 2;;
	--raw-entry-point)			# Entry point address for binary created from raw data.
		[ "$RAW_ENTRY_POINT" ] && print_error_and_die "Duplicate option: --raw-entry-point"
		RAW_ENTRY_POINT="$2"
		#RAW_ENTRY_POINT="$(($2))"  # evaluate hex address - probably not needed
		shift 2;;
	--raw-section-vma)			# Virtual memory address for section created from raw data.
		[ "$RAW_SECTION_VMA" ] && print_error_and_die "Duplicate option: --raw-section-vma"
		RAW_SECTION_VMA="$2"
		#RAW_SECTION_VMA="$(($2))"  # evaluate hex address - probably not needed
		shift 2;;
	--cleanup) 						# Cleanup.
		[ "$CLEANUP" ] && print_error_and_die "Duplicate option: --cleanup"
		CLEANUP=1
		shift;;
	--color-for-ida)
		[ "$COLOR_IDA" ] && print_error_and_die "Duplicate option: --color-for-ida"
		COLOR_IDA=1
		shift;;
	--config)
		[ "$CONFIG_DB" ] && print_error_and_die "Duplicate option: --config"
		[ "$NO_CONFIG" ] && print_error_and_die "Option --config can not be used with option --no-config"
		CONFIG_DB="$2"
		if [ ! -r "$CONFIG_DB" ]; then
			print_error_and_die "The input JSON configuration file '$CONFIG_DB' does not exist or is not readable"
		fi
		shift 2;;
	--no-config)
		[ "$NO_CONFIG" ] && print_error_and_die "Duplicate option: --no-config"
		[ "$CONFIG_DB" ] && print_error_and_die "Option --no-config can not be used with option --config"
		NO_CONFIG=1
		shift;;
	--graph-format)					# Format of graph files.
		[ "$GRAPH_FORMAT" ] && print_error_and_die "Duplicate option: --graph-format"
		[ "$2" != "pdf" -a "$2" != "png" -a "$2" != "svg" ] && print_error_and_die "Unsupported graph format '$2'. Supported formats: pdf, png, svg."
		GRAPH_FORMAT="$2"
		shift 2;;
	--select-decode-only)
		[ "$SELECTED_DECODE_ONLY" ] && print_error_and_die "Duplicate option: --select-decode-only"
		SELECTED_DECODE_ONLY=1
		shift;;
	--select-functions)				# List of selected functions.
		[ "$SELECTED_FUNCTIONS" ] && print_error_and_die "Duplicate option: --select-functions"
		IFS=',' read -a SELECTED_FUNCTIONS <<< "$2"  # parser line into array
		KEEP_UNREACHABLE_FUNCS=1
		shift 2;;
	--select-ranges)				# List of selected ranges.
		[ "$SELECTED_RANGES" ] && print_error_and_die "Duplicate option: --select-ranges"
		SELECTED_RANGES="$2"
		IFS=',' read -a SELECTED_RANGES <<< "$2"  # parser line into array
		KEEP_UNREACHABLE_FUNCS=1
		shift 2;;
	--stop-after) 				# Stop decompilation after the given tool.
		[ "$STOP_AFTER" ] && print_error_and_die "Duplicate option: --stop-after"
		STOP_AFTER="$2"
		if [[ ! "$STOP_AFTER" =~ ^(fileinfo|unpacker|bin2llvmir|llvmir2hll)$ ]]; then
			print_error_and_die "Unsupported tool '$STOP_AFTER' for --stop-after"
		fi
		shift 2;;
	--static-code-sigfile) 			# User provided signature file.
		[ ! -f "$2" ] && print_error_and_die "Invalid .yara file '$2'"
		TEMPORARY_SIGNATURES+=("$2")
		shift 2;;
	--static-code-archive) 			# User provided archive to create signature file from.
		[ ! -f "$2" ] && print_error_and_die "Invalid archive file '$2'"
		SIGNATURE_ARCHIVE_PATHS+=("$2")
		shift 2;;
	--no-default-static-signatures)
		DO_NOT_LOAD_STATIC_SIGNATURES=1
		shift;;
	--fileinfo-verbose)				# Enable --verbose mode in fileinfo.
		[ "$FILEINFO_VERBOSE" ] && print_error_and_die "Duplicate option: --fileinfo-verbose"
		FILEINFO_VERBOSE=1
		shift;;
	--fileinfo-use-all-external-patterns)
		[ "$FILEINFO_USE_ALL_EXTERNAL_PATTERNS" ] && print_error_and_die "Duplicate option: --fileinfo-use-all-external-patterns"
		FILEINFO_USE_ALL_EXTERNAL_PATTERNS=1
		shift;;
	--ar-name)						# Archive decompilation by name.
		[ "$AR_NAME" ] && print_error_and_die "Duplicate option: --ar-name"
		AR_NAME="$2"
		shift 2;;
	--ar-index)						# Archive decompilation by index.
		[ "$AR_INDEX" ] && print_error_and_die "Duplicate option: --ar-index"
		AR_INDEX="$2"
		shift 2;;
	--)								# Input file.
		if [ $# -eq 2 ]; then
			IN="$2"
			if [ ! -r "$IN" ]; then
				print_error_and_die "The input file '$IN' does not exist or is not readable"
			fi
		elif [ $# -gt 2 ]; then		# Invalid options.
			print_error_and_die "Invalid options: '$2', '$3' ..."
		fi
		break;;
	esac
done

# Check arguments and set default values for unset options.
check_arguments

if [ "$MODE" = "raw" ]; then

	##
	## Default values and initialization.
	##
	OUT_RAW_EXECUTABLE="$IN"

	# Entry point for THUMB must be odd.
	[ "$ARCH" = "thumb" ] && [ $((RAW_ENTRY_POINT % 2)) -eq 0 ] && RAW_ENTRY_POINT=$((RAW_ENTRY_POINT+1))

	# Enable binary decompilation.
	IN="$OUT_RAW_EXECUTABLE"
	KEEP_UNREACHABLE_FUNCS=1
fi

# Check for archives.
if [ "$MODE" = "bin" ]; then

	# Check for archives packed in Mach-O Universal Binaries.
	echo "##### Checking if file is a Mach-O Universal static library..."
	echo "RUN: $EXTRACT --list $IN"
	if is_macho_archive "$IN"; then
		OUT_ARCHIVE="$OUT.a"
		if [ "$ARCH" ]; then
			echo ""
			echo "##### Restoring static library with architecture family $ARCH..."
			echo "RUN: $EXTRACT --family "$ARCH" --out "$OUT_ARCHIVE" "$IN""
			if ! $EXTRACT --family "$ARCH" --out "$OUT_ARCHIVE" "$IN"; then
				# Architecture not supported
				echo "Invalid --arch option \"$ARCH\". File contains these architecture families:"
				$EXTRACT --list "$IN"
				cleanup
				exit 1
			fi
		else
			# Pick best architecture
			echo ""
			echo "##### Restoring best static library for decompilation..."
			echo "RUN: $EXTRACT --best --out "$OUT_ARCHIVE" "$IN""
			$EXTRACT --best --out "$OUT_ARCHIVE" "$IN"
		fi
		IN="$OUT_ARCHIVE"
	fi

	echo ""
	echo "##### Checking if file is an archive..."
	echo "RUN: $AR --arch-magic $IN"
	if has_archive_signature "$IN"; then
		echo "This file is an archive!"
		# Check for thin signature.
		if has_thin_archive_signature "$IN"; then
			cleanup
			print_error_and_die "File is a thin archive and cannot be decompiled."
		fi
		# Check if our tools can handle it.
		if ! is_valid_archive "$IN"; then
			cleanup
			print_error_and_die "The input archive has invalid format."
		fi
		# Get and check number of objects.
		ARCH_OBJECT_COUNT=$(archive_object_count "$IN")
		if [ "$ARCH_OBJECT_COUNT" -le "0" ]; then
			cleanup
			print_error_and_die "The input archive is empty."
		fi
		# Prepare object output path.
		OUT_RESTORED="$OUT.restored"
		# Pick object by index.
		if [ "$AR_INDEX" ]; then
			echo ""
			echo "##### Restoring object file on index '$AR_INDEX' from archive..."
			echo "RUN: $AR $IN --index $AR_INDEX --output $OUT_RESTORED"
			if  ! archive_get_by_index "$IN" "$AR_INDEX" "$OUT_RESTORED"; then
				cleanup
				VALID_INDEX=$(( ARCH_OBJECT_COUNT - 1 ))
				if [ "$VALID_INDEX" -ne "0" ]; then
					print_error_and_die "File on index \"$AR_INDEX\" was not found in the input archive. Valid indexes are 0-$VALID_INDEX."
				else
					print_error_and_die "File on index \"$AR_INDEX\" was not found in the input archive. The only valid index is 0."
				fi
			fi
			IN="$OUT_RESTORED"
		# Pick object by name.
		elif [ "$AR_NAME" ]; then
			echo ""
			echo "##### Restoring object file with name '$AR_NAME' from archive..."
			echo "RUN: $AR $IN --name $AR_NAME --output $OUT_RESTORED"
			if  ! archive_get_by_name "$IN" "$AR_NAME" "$OUT_RESTORED"; then
				cleanup
				print_error_and_die "File named \"$AR_NAME\" was not found in the input archive."
			fi
			IN="$OUT_RESTORED"
		else
			# Print list of files.
			echo "Please select file to decompile with either '--ar-index=n'"
			echo "or '--ar-name=string' option. Archive contains these files:"
			archive_list_numbered_content "$IN"
			cleanup
			exit 1
		fi
	else
		[ "$AR_NAME" ] && print_warning "Option --ar-name can be used only with archives."
		[ "$AR_INDEX" ] && print_warning "Option --ar-index can be used only with archives."
		echo "Not an archive, going to the next step."
	fi
fi

if [ "$MODE" = "bin" ] || [ "$MODE" = "raw" ]; then
	# Assignment of other used variables.
	OUT_UNPACKED="${OUT%.*}-unpacked"
	OUT_FRONTEND="$OUT.frontend"
	OUT_FRONTEND_LL="$OUT_FRONTEND.ll"
	OUT_FRONTEND_BC="$OUT_FRONTEND.bc"

	CONFIG="$OUT.json"

	if [ "$CONFIG" != "$CONFIG_DB" ]; then
		rm -f "$CONFIG"
	fi
	if [ "$CONFIG_DB" ]; then
		cp "$CONFIG_DB" "$CONFIG"
	fi

	# Preprocess existing file or create a new, empty JSON file.
	if [ -f "$CONFIG" ]; then
		$CONFIGTOOL "$CONFIG" --preprocess
	else
		echo "{}" > "$CONFIG"
	fi

	# Raw data needs architecture, endianess and optionaly sections's vma and entry point to be specified.
	if [ "$MODE" = "raw" ]; then
		[ ! "$ARCH" -o "$ARCH" = "unknown" -o "$ARCH" = "" ] && print_error_and_die "Option -a|--arch must be used with format $FORMAT"
		[ ! "$ENDIAN" ] && print_error_and_die "Option -e|--endian must be used with format $FORMAT"
		$CONFIGTOOL "$CONFIG" --write --format "raw"
		$CONFIGTOOL "$CONFIG" --write --arch "$ARCH"
		$CONFIGTOOL "$CONFIG" --write --bit-size 32
		$CONFIGTOOL "$CONFIG" --write --file-class 32
		$CONFIGTOOL "$CONFIG" --write --endian "$ENDIAN"

		if [ "$RAW_ENTRY_POINT" ]; then
			$CONFIGTOOL "$CONFIG" --write --entry-point "$RAW_ENTRY_POINT"
		fi

		if [ "$RAW_SECTION_VMA" ]; then
			$CONFIGTOOL "$CONFIG" --write --section-vma "$RAW_SECTION_VMA"
		fi
	fi

	##
	## Call fileinfo to create an initial config file.
	##
	FILEINFO_PARAMS=(-c "$CONFIG" --similarity "$IN" --no-hashes=all)
	if [ "$FILEINFO_VERBOSE" ]; then
		FILEINFO_PARAMS=(-c "$CONFIG" --similarity --verbose "$IN")
	fi
	for par in "${FILEINFO_EXTERNAL_YARA_PRIMARY_CRYPTO_DATABASES[@]}"; do
		FILEINFO_PARAMS+=(--crypto "$par")
	done
	if [ "$FILEINFO_USE_ALL_EXTERNAL_PATTERNS" ]; then
		for par in "${FILEINFO_EXTERNAL_YARA_EXTRA_CRYPTO_DATABASES[@]}"; do
			FILEINFO_PARAMS+=(--crypto "$par")
		done
	fi
	echo ""
	echo "##### Gathering file information..."
	echo "RUN: $FILEINFO ${FILEINFO_PARAMS[@]}"

	$FILEINFO "${FILEINFO_PARAMS[@]}"
	FILEINFO_RC=$?

	if [ "$FILEINFO_RC" -ne 0 ]; then
		cleanup
		# The error message has been already reported by fileinfo in stderr.
		print_error_and_die
	fi
	check_whether_decompilation_should_be_forcefully_stopped "fileinfo"

	##
	## Unpacking.
	##
	UNPACK_PARAMS=(--extended-exit-codes --output "$OUT_UNPACKED" "$IN")
	$UNPACK_SH "${UNPACK_PARAMS[@]}"
	UNPACKER_RC=$?

	check_whether_decompilation_should_be_forcefully_stopped "unpacker"

	# RET_UNPACK_OK=0
	# RET_UNPACKER_NOTHING_TO_DO_OTHERS_OK=1
	# RET_NOTHING_TO_DO=2
	# RET_UNPACKER_FAILED_OTHERS_OK=3
	# RET_UNPACKER_FAILED=4
	if [ "$UNPACKER_RC" -eq 0 ] || [ "$UNPACKER_RC" -eq 1 ] || [ "$UNPACKER_RC" -eq 3 ] ; then
		# Successfully unpacked -> re-run fileinfo to obtain fresh information.
		IN="$OUT_UNPACKED"
		FILEINFO_PARAMS=(-c "$CONFIG" --similarity "$IN" --no-hashes=all)
		if [ "$FILEINFO_VERBOSE" ]; then
			FILEINFO_PARAMS=(-c "$CONFIG" --similarity --verbose "$IN")
		fi
		for par in "${FILEINFO_EXTERNAL_YARA_PRIMARY_CRYPTO_DATABASES[@]}"; do
			FILEINFO_PARAMS+=(--crypto "$par")
		done
		if [ "$FILEINFO_USE_ALL_EXTERNAL_PATTERNS" ]; then
			for par in "${FILEINFO_EXTERNAL_YARA_EXTRA_CRYPTO_DATABASES[@]}"; do
				FILEINFO_PARAMS+=(--crypto "$par")
			done
		fi

		echo ""
		echo "##### Gathering file information after unpacking..."
		echo "RUN: $FILEINFO ${FILEINFO_PARAMS[@]}"

		$FILEINFO "${FILEINFO_PARAMS[@]}"
		FILEINFO_RC=$?

		if [ $FILEINFO_RC -ne 0 ]; then
			cleanup
			# The error message has been already reported by fileinfo in stderr.
			print_error_and_die
		fi

		print_warning_if_decompiling_bytecode
	fi

	# Check whether the architecture was specified.
	if [ "$ARCH" ]; then
		$CONFIGTOOL "$CONFIG" --write --arch "$ARCH"
	else
		# Get full name of the target architecture including comments in parentheses
		ARCH_FULL=$($CONFIGTOOL "$CONFIG" --read --arch | awk '{print tolower($0)}')
		# Strip comments in parentheses and all trailing whitespace
		ARCH="$(echo ${ARCH_FULL%(*} | sed -e 's/^[[:space:]]*//')"
	fi

	# Check whether the file format was specified.
	if [ "$FORMAT" ]; then
		$CONFIGTOOL "$CONFIG" --write --format "$FORMAT"
	else
		FORMAT=$($CONFIGTOOL "$CONFIG" --read --format | awk '{print tolower($1);}')
	fi

	# Intel HEX needs architecture to be specified
	if [ "$FORMAT" = "ihex" ]; then
		[ ! "$ARCH" -o "$ARCH" = "unknown" -o "$ARCH" = "" ] && print_error_and_die "Option -a|--arch must be used with format $FORMAT"
		[ ! "$ENDIAN" ] && print_error_and_die "Option -e|--endian must be used with format $FORMAT"
		$CONFIGTOOL "$CONFIG" --write --arch "$ARCH"
		$CONFIGTOOL "$CONFIG" --write --bit-size 32
		$CONFIGTOOL "$CONFIG" --write --file-class 32
		$CONFIGTOOL "$CONFIG" --write --endian "$ENDIAN"
	fi

	# Check whether the correct target architecture was specified.
	if [ "$ARCH" = "arm" -o "$ARCH" = "thumb" ]; then
		ORDS_DIR="$ARM_ORDS_DIR"
	elif [ "$ARCH" = "x86" ]; then
		ORDS_DIR="$X86_ORDS_DIR"
	elif [ "$ARCH" = "powerpc" -o "$ARCH" = "mips" -o "$ARCH" = "pic32" ]; then
		: # nothing
	else
		cleanup
		print_error_and_die "Unsupported target architecture '${ARCH^^}'. Supported architectures: Intel x86, ARM, ARM+Thumb, MIPS, PIC32, PowerPC."
	fi

	# Check file class (e.g. "ELF32", "ELF64"). At present, we can only decompile 32-bit files.
	# Note: we prefer to report the "unsupported architecture" error (above) than this "generic" error.
	FILECLASS=$($CONFIGTOOL "$CONFIG" --read --file-class)
	if [ "$FILECLASS" != "16" ] && [ "$FILECLASS" != "32" ]; then
		cleanup
		print_error_and_die "Unsupported target format '${FORMAT^^}$FILECLASS'. Supported formats: ELF32, PE32, Intel HEX 32."
	fi

	# Set path to statically linked code signatures.
	#
	# TODO: Useing ELF for IHEX is ok, but for raw, we probably should somehow decide between ELF and PE, or use both, for RAW.
	SIG_FORMAT=${FORMAT,,}
	if [ "$SIG_FORMAT" == "ihex" ] || [ "$SIG_FORMAT" == "raw" ]; then
		SIG_FORMAT="elf"
	fi
	ENDIAN=$($CONFIGTOOL "$CONFIG" --read --endian)
	case "$ENDIAN" in
		"little") SIG_ENDIAN="le" ;;
		"big") SIG_ENDIAN="be" ;;
		*) SIG_ENDIAN="" ;;
	esac
	SIG_ARCH=${ARCH,,}
	if [ "$SIG_ARCH" == "pic32" ]; then
		SIG_ARCH="mips"
	fi
	SIGNATURES_DIR="$GENERIC_SIGNATURES_DIR/$SIG_FORMAT/${FILECLASS,,}/${SIG_ENDIAN,,}/$SIG_ARCH"

	print_warning_if_decompiling_bytecode

	# Decompile unreachable functions.
	if [ "$KEEP_UNREACHABLE_FUNCS" ]; then
		$CONFIGTOOL "$CONFIG" --write --keep-unreachable-funcs "true"
	fi

	# Get signatures from selected archives.
	if [ "${#SIGNATURE_ARCHIVE_PATHS[@]}" -ne 0 ]; then
		echo ""
		echo "##### Extracting signatures from selected archives..."
	fi
	for ((l = 0; l < ${#SIGNATURE_ARCHIVE_PATHS[@]}; l++)); do
		LIB="${SIGNATURE_ARCHIVE_PATHS[$l]}"
		echo "Extracting signatures from file '$LIB'"
		CROP_ARCH_PATH=$(basename "$LIB" | LC_ALL=C sed -e 's/[^A-Za-z0-9_.-]/_/g')
		SIG_OUT="$OUT.$CROP_ARCH_PATH.$l.yara"
		if $SIG_FROM_LIB_SH "$LIB" --output "$SIG_OUT" &> "$DEV_NULL" ; then
			$CONFIGTOOL "$CONFIG" --write --user-signature "$SIG_OUT"
			SIGNATURES_TO_REMOVE+=("$SIG_OUT")
		else
			print_warning "Failed extracting signatures from file \"$LIB\""
		fi
	done

	# Store paths of signature files into config for frontend.
	[ "$DO_NOT_LOAD_STATIC_SIGNATURES" ] || $CONFIGTOOL "$CONFIG" --write --signatures "$SIGNATURES_DIR"
	# User provided signatures.
	for i in "${TEMPORARY_SIGNATURES[@]}"; do
		$CONFIGTOOL "$CONFIG" --write --user-signature "$i"
	done

	# Store paths of type files into config for frontend.
	if [ -d "$GENERIC_TYPES_DIR" ]; then
		$CONFIGTOOL "$CONFIG" --write --types "$GENERIC_TYPES_DIR"
	fi

	# Store path of directory with ORD files into config for frontend (note: only directory, not files themselves).
	if [ -d "$ORDS_DIR" ]; then
		$CONFIGTOOL "$CONFIG" --write --ords "$ORDS_DIR/"
	fi

	# Store paths to file with PDB debugging information into config for frontend.
	if [ -e "$PDB_FILE" ]; then
		$CONFIGTOOL "$CONFIG" --write --pdb-file "$PDB_FILE"
	fi

	# Store file names of input and output into config for frontend.
	$CONFIGTOOL "$CONFIG" --write --input-file "$IN"
	$CONFIGTOOL "$CONFIG" --write --frontend-output-file "$OUT_FRONTEND_LL"
	$CONFIGTOOL "$CONFIG" --write --output-file "$OUT"

	# Store decode only selected parts flag.
	if [ "$SELECTED_DECODE_ONLY" ]; then
		$CONFIGTOOL "$CONFIG" --write --decode-only-selected "true"
	else
		$CONFIGTOOL "$CONFIG" --write --decode-only-selected "false"
	fi

	# Store selected functions or selected ranges into config for frontend.
	if [ "$SELECTED_FUNCTIONS" ]; then
		for f in "${SELECTED_FUNCTIONS[@]}"; do
			$CONFIGTOOL "$CONFIG" --write --selected-func "$f"
		done
	fi
	if [ "$SELECTED_RANGES" ]; then
		for r in "${SELECTED_RANGES[@]}"; do
			$CONFIGTOOL "$CONFIG" --write --selected-range "$r"
		done
	fi

	# Assignment of other used variables.
	# We have to ensure that the .bc version of the decompiled .ll file is placed
	# in the same directory as are other output files. Otherwise, there may be
	# race-condition problems when the same input .ll file is decompiled in
	# parallel processes because they would overwrite each other's .bc file. This
	# is most likely to happen in regression tests in the "ll" mode.
	OUT_BACKEND="$OUT.backend"
	# If the input file is the same as $OUT_BACKEND_LL below, then we have to change the name of
	# $OUT_BACKEND. Otherwise, the input file would get overwritten during the conversion.
	[ "$OUT_FRONTEND_LL" = "$OUT_BACKEND.ll" ] && OUT_BACKEND="$OUT.backend.backend"
	OUT_BACKEND_BC="$OUT_BACKEND.bc"
	OUT_BACKEND_LL="$OUT_BACKEND.ll"

	##
	## Decompile the binary into LLVM IR.
	##
	if [ "$KEEP_UNREACHABLE_FUNCS" ]; then
		# Prevent bin2llvmir from removing unreachable functions.
		BIN2LLVMIR_PARAMS="$(sed 's/-unreachable-funcs *//g' <<< "$BIN2LLVMIR_PARAMS")"
	fi

	if [ "$CONFIG" = "" ] && [ "$CONFIG_DB" != "" ]; then
		CONFIG="$CONFIG_DB"
	fi

	BIN2LLVMIR_PARAMS=(-provider-init -config-path "$CONFIG" -decoder $BIN2LLVMIR_PARAMS)

	echo ""
	echo "##### Decompiling $IN into $OUT_BACKEND_BC..."
	echo "RUN: $BIN2LLVMIR ${BIN2LLVMIR_PARAMS[@]} -o $OUT_BACKEND_BC"

	$BIN2LLVMIR "${BIN2LLVMIR_PARAMS[@]}" -o "$OUT_BACKEND_BC"
	BIN2LLVMIR_RC=$?

	if [ "$BIN2LLVMIR_RC" -ne 0 ]; then
		cleanup
		print_error_and_die "Decompilation to LLVM IR failed"
	fi
	check_whether_decompilation_should_be_forcefully_stopped "bin2llvmir"

fi # modes "bin" || "raw"

# LL mode goes straight to backend.
if [ "$MODE" = "ll" ]; then
	OUT_BACKEND_BC="$IN"
	CONFIG="$CONFIG_DB"
fi

# Conditional initialization.
BACKEND_VAR_RENAMER=${BACKEND_VAR_RENAMER:=readable}
BACKEND_CALL_INFO_OBTAINER=${BACKEND_CALL_INFO_OBTAINER:=optim}
BACKEND_ARITHM_EXPR_EVALUATOR=${BACKEND_ARITHM_EXPR_EVALUATOR:=c}
BACKEND_LLVMIR2BIR_CONVERTER=${BACKEND_LLVMIR2BIR_CONVERTER:=orig}

# Create parameters for the $LLVMIR2HLL call.
LLVMIR2HLL_PARAMS=(-target-hll="$HLL" -var-renamer="$BACKEND_VAR_RENAMER" -var-name-gen=fruit -var-name-gen-prefix= -call-info-obtainer="$BACKEND_CALL_INFO_OBTAINER" -arithm-expr-evaluator="$BACKEND_ARITHM_EXPR_EVALUATOR" -validate-module -llvmir2bir-converter="$BACKEND_LLVMIR2BIR_CONVERTER" -o "$OUT" "$OUT_BACKEND_BC")
[ -z "$BACKEND_NO_DEBUG" ] && LLVMIR2HLL_PARAMS+=(-enable-debug)
[ -z "$BACKEND_NO_DEBUG_COMMENTS" ] && LLVMIR2HLL_PARAMS+=(-emit-debug-comments)
[ "$CONFIG" ] && LLVMIR2HLL_PARAMS+=(-config-path="$CONFIG")
[ "$KEEP_UNREACHABLE_FUNCS" ] && LLVMIR2HLL_PARAMS+=(-keep-unreachable-funcs)
[ "$BACKEND_SEMANTICS" ] && LLVMIR2HLL_PARAMS+=(-semantics "$BACKEND_SEMANTICS")
[ "$BACKEND_ENABLED_OPTS" ] && LLVMIR2HLL_PARAMS+=(-enabled-opts="$BACKEND_ENABLED_OPTS")
[ "$BACKEND_DISABLED_OPTS" ] && LLVMIR2HLL_PARAMS+=(-disabled-opts="$BACKEND_DISABLED_OPTS")
[ "$BACKEND_OPTS_DISABLED" ] && LLVMIR2HLL_PARAMS+=(-no-opts)
[ "$BACKEND_AGGRESSIVE_OPTS" ] && LLVMIR2HLL_PARAMS+=(-aggressive-opts)
[ "$BACKEND_VAR_RENAMING_DISABLED" ] && LLVMIR2HLL_PARAMS+=(-no-var-renaming)
[ "$BACKEND_NO_SYMBOLIC_NAMES" ] && LLVMIR2HLL_PARAMS+=(-no-symbolic-names)
[ "$BACKEND_KEEP_ALL_BRACKETS" ] && LLVMIR2HLL_PARAMS+=(-keep-all-brackets)
[ "$BACKEND_KEEP_LIBRARY_FUNCS" ] && LLVMIR2HLL_PARAMS+=(-keep-library-funcs)
[ "$BACKEND_NO_TIME_VARYING_INFO" ] && LLVMIR2HLL_PARAMS+=(-no-time-varying-info)
[ "$BACKEND_NO_COMPOUND_OPERATORS" ] && LLVMIR2HLL_PARAMS+=(-no-compound-operators)
[ "$BACKEND_FIND_PATTERNS" ] && LLVMIR2HLL_PARAMS+=(-find-patterns "$BACKEND_FIND_PATTERNS")
[ "$BACKEND_EMIT_CG" ] && LLVMIR2HLL_PARAMS+=(-emit-cg)
[ "$BACKEND_FORCED_MODULE_NAME" ] && LLVMIR2HLL_PARAMS+=(-force-module-name="$BACKEND_FORCED_MODULE_NAME")
[ "$BACKEND_STRICT_FPU_SEMANTICS" ] && LLVMIR2HLL_PARAMS+=(-strict-fpu-semantics)
if [ "$BACKEND_EMIT_CFG" ]; then
	LLVMIR2HLL_PARAMS+=(-emit-cfgs)
	[ "$BACKEND_CFG_TEST" ] && LLVMIR2HLL_PARAMS+=(--backend-cfg-test)
fi

##
## Decompile the optimized IR code.
##
echo ""
echo "##### Decompiling $OUT_BACKEND_BC into $OUT..."
echo "RUN: $LLVMIR2HLL ${LLVMIR2HLL_PARAMS[@]}"

$LLVMIR2HLL "${LLVMIR2HLL_PARAMS[@]}"
LLVMIR2HLL_RC=$?

if [ "$LLVMIR2HLL_RC" -ne 0 ]; then
	cleanup
	print_error_and_die "Decompilation of file '$OUT_BACKEND_BC' failed"
fi
check_whether_decompilation_should_be_forcefully_stopped "llvmir2hll"

# Conditional initialization.
GRAPH_FORMAT=${GRAPH_FORMAT:=png}
BACKEND_CG_CONVERSION=${BACKEND_CG_CONVERSION:=auto}
BACKEND_CFG_CONVERSION=${BACKEND_CFG_CONVERSION:=auto}
##
## Convert .dot graphs to desired format.
##
if [[ ( "$BACKEND_EMIT_CG" && "$BACKEND_CG_CONVERSION" = "auto" ) ||
		( "$BACKEND_EMIT_CFG" && "$BACKEND_CFG_CONVERSION" = "auto" ) ]]; then
	echo ""
	echo "##### Converting .dot files to the desired format..."
fi
if [ "$BACKEND_EMIT_CG" ] && [ "$BACKEND_CG_CONVERSION" = "auto" ]; then
	echo "RUN: dot -T$GRAPH_FORMAT $OUT.cg.dot > $OUT.cg.$GRAPH_FORMAT"
	dot -T"$GRAPH_FORMAT" "$OUT".cg.dot > "$OUT".cg."$GRAPH_FORMAT"
fi
if [ "$BACKEND_EMIT_CFG" ] && [ "$BACKEND_CFG_CONVERSION" = "auto" ]; then
	for cfg in "$OUT".cfg.*.dot; do
		echo "RUN: dot -T$GRAPH_FORMAT $cfg > ${cfg%.*}.$GRAPH_FORMAT"
		dot -T"$GRAPH_FORMAT" "$cfg" > "${cfg%.*}.$GRAPH_FORMAT"
	done
fi

# Remove trailing whitespace and the last redundant empty new line from the
# generated output (if any). It is difficult to do this in the back-end, so we
# do it here.
sed -i 's/[ \t]*$//' "$OUT"
sed -i '$ { /^$/ d}' "$OUT"

##
## Colorize output file.
##
if [ "$COLOR_IDA" ]; then
	$IDA_COLORIZER "$OUT" "$CONFIG"
fi

# Success!
cleanup
echo ""
echo "##### Done!"
